/*
 * @(#)GateTagger.java	1.0 10/01/07
 *
 * Copyright 2007 Fabio Gasparetti. All rights reserved.
 */

package org.tabularium.text.nlp;

import gate.Annotation;
import gate.AnnotationSet;
import gate.Corpus;
import gate.Document;
import gate.Factory;
import gate.FeatureMap;
import gate.Gate;
import gate.GateConstants;
import gate.ProcessingResource;
import gate.corpora.RepositioningInfo;
import gate.creole.ANNIEConstants;
import gate.creole.SerialAnalyserController;
import gate.util.GateException;

import org.tabularium.text.nlp.wordnet.PartOfSpeech;

import java.io.File;
import java.util.*;
import java.util.prefs.*;

/**
 * Performs a textual similarity based on a Lexical dictionary of relations
 * among terms/synsets.
 * <p>
 * Some of the entity recognized by ANNIE: 
 * <ul>
 * <li>ADDRESS - feature 'kind' = email for emails
 * <li>DATE - Used to classify a reference to a date or period. Ex: November 2
 * 2001, January, Monday, 60���s, 1940���s, several years. Ex: two months, 4 months,
 * last week, last year, yesterday, tomorrow, spring, summer, fall, winter,
 * fourth quarter, first quarter, today, etc. Age also falls under this
 * category, even when it���s a noun phrase referring to a person: the 5-year-old,
 * 5 years old, Jane Doe, 5, etc.
 * For times, we have the feature kind = time.
 * <li>LOCATION - Names of countries, cities, states, provinces, municipalities
 * or toher names of locations, e.g. mountain ranges, coasts, borders, planets,
 * geocoordinates, bodies of water. Also included in this category are named
 * regions such as the Middle East, areas, neighborhoods, continents and regions
 * of continents such as Eastern Europe.
 * <li>ORGANIZATION - Markable: Names of companies, government agencies,
 * educational institutions and other institutions: sport teams, hospitals,
 * museums, and libraries. White House, Pentagon, Kremlin are ORG names as is
 * Capitol Hill. Adjectival forms of organization names are also included:
 * Congressional efforts, etc.
 * Feature orgType = company, what are the others?
 * <li>PERSON - Proper names of people, including the following: fictional
 * people, first names, last names, individual or family names, unique
 * nicknames. Generational markers such as Jr. and IV, are included (Ray Garrett
 * Jr., Henry IV).
 * <li>TITLE - Any head word of a common noun referring to a person or group of
 * people. Any occupational title in modifier position. For example, for
 * ���President Bush��� we have a descriptor (���president���) and a name (���Bush���).
 * </ul>
 * A typical invocation sequence is:
 * <blockquote><pre>
 * GateTagger tagger = new GateTagger();
 * tagger.init("/home/fabio/projects/GATE-4.0"); 
 * Tag[] tags = tagger.tag("text to tag");
 * </pre></blockquot>
 *
 * Descriptions taken from {@link http://www.ldc.upenn.edu/Catalog/docs/LDC2005T33/BBN-Types-Subtypes.html}
 *
 * @author Fabio Gasparetti
 * @version 1.0, 10/01/07
 */
public class GateTagger extends TextTagger {
	// translate brown corpus tags into basic POS
	protected static final String[] VERB = { "VB", "VBD", "VBG", "VBN", "VBP", "VBZ", "MD"  };
	protected static final String[] UNC = { "" };
	protected static final String[] NOUN = { "NN", "NN$", "NNS", "NNS$", "NP", "NP$", "NPS", "NPS$", "NNP", "NNPS", "NR", "NR$", "NRS" };
	protected static final String[] STOP = { "" };
	protected static final String[] PRON = { "WP", "WP$", "PRP", "PRP$", "PN", "PN$", "PP$$", "PPL", "PPLS", "PPO", "PPS", "PPSS" };
	protected static final String[] PREP = { "" };
	protected static final String[] INTERJ = { "UH" };
	protected static final String[] CONJ = { "CC", "IN" };
	protected static final String[] ART = { "AT", "DT", "WDT" };
	protected static final String[] ADV = { "WRB", "RB", "RB$", "RBR", "RBS", "RBT", "RN", "RP" };
	protected static final String[] ADJ = { "JJ", "JJ$", "JJR", "JJS", "JJT" };
	
	protected static String[][] POS_CLASSES; /*= { VERB, UNC, NOUN, STOP, PRON, PREP, INTERJ, CONJ, ART, ADV, ADJ }*/
	
	protected Map posConversionMap = null;

	/*
	public BncParser() {
	}
	*/
	
	/** The Corpus Pipeline application to contain ANNIE */
	private SerialAnalyserController annieController;

	/**
	 * Initializes with the file system location of GATE.
	 */
	public void init(String gateHome) throws Exception {
		// pos conversion (from BROWN TAG to PartOfSpeech integer) 
		POS_CLASSES = new String[PartOfSpeech.MAX_POS][];
		for (int i = 0; i < POS_CLASSES.length; i++) 
			POS_CLASSES[i] = null;		
		POS_CLASSES[PartOfSpeech.VERB] = VERB;
		POS_CLASSES[PartOfSpeech.NOUN] = NOUN;
		POS_CLASSES[PartOfSpeech.PRONOUN] = PRON;
		POS_CLASSES[PartOfSpeech.INTERJECTION] = INTERJ;
		POS_CLASSES[PartOfSpeech.CONJUCTION] = CONJ;
		POS_CLASSES[PartOfSpeech.ARTICLE] = ART;
		POS_CLASSES[PartOfSpeech.ADVERB] = ADV;
		POS_CLASSES[PartOfSpeech.ADJECTIVE] = ADJ;
		posConversionMap = new HashMap();
		for (int i = 0; i < POS_CLASSES.length; i++) {
			if (POS_CLASSES[i] == null)
				continue;
			for (int j = 0; j < POS_CLASSES[i].length; j++) 
				posConversionMap.put(POS_CLASSES[i][j], new Integer(i));				
		}
		if (Gate.getGateHome() == null)
			Gate.setGateHome(new File(gateHome));
		
		Gate.init();
		// Load ANNIE plugin
		File pluginsHome = new File(gateHome, "plugins");
		Gate.getCreoleRegister().registerDirectories(
				new File(pluginsHome, "ANNIE").toURL());
		initAnnie();
	}

	/**
	 * Initializes retrieving the file system location of GATE from
	 * user's preferences.
	 */
	public void init() throws Exception {
		File f = Gate.getGateHome();
		if (f == null) {
			Preferences prefs = Preferences.userRoot().node("/gate");
			String gh = prefs.get("home",
					"/home/fabio/Projects/GATE-4.0");
			f = new File(gh);
		}
		this.init(f.toString());
	}

	/**
	 * Initialise the ANNIE system. This creates a "corpus pipeline" application
	 * that can be used to run sets of documents through the extraction system.
	 */
	protected void initAnnie() throws GateException {
		// create a serial analyser controller to run ANNIE with
		annieController = (SerialAnalyserController) Factory.createResource(
				"gate.creole.SerialAnalyserController",
				Factory.newFeatureMap(), Factory.newFeatureMap(), "ANNIE_"
						+ Gate.genSym());

		// load each PR as defined in ANNIEConstants
		for (int i = 0; i < ANNIEConstants.PR_NAMES.length; i++) {
			FeatureMap params = Factory.newFeatureMap(); // use default
			// parameters
			ProcessingResource pr = (ProcessingResource) Factory
					.createResource(ANNIEConstants.PR_NAMES[i], params);

			// add the PR to the pipeline controller
			annieController.add(pr);
		} // for each ANNIE PR

	} // initAnnie()

	/**
	 * If the given text has length 0 or it is composed of spaces, an exception
	 * occurs.
	 * 
	 * Note: works only for tag lengths less than INT_MAX
	 */
	public Tag[] tag(String text) throws Exception {
		ArrayList tags = new ArrayList();

		Corpus corpus = (Corpus) Factory
				.createResource("gate.corpora.CorpusImpl");
		Document doc = (Document) Factory.newDocument(text);
		FeatureMap params = Factory.newFeatureMap();
		params.put("preserveOriginalContent", new Boolean(true));
		params.put("collectRepositioningInfo", new Boolean(true));
		doc.setFeatures(params);
		/*
		 * Document doc = (Document) Factory.createResource(
		 * "gate.corpora.DocumentImpl", params);
		 */
		corpus.add(doc);
		annieController.setCorpus(corpus);
		annieController.execute();

		AnnotationSet annotSet = doc.getAnnotations();
		/*
		 * Set annotTypesRequired = new HashSet();
		 * annotTypesRequired.add("Person"); annotTypesRequired.add("Location");
		 * AnnotationSet peopleAndPlaces = defaultAnnotSet
		 * .get(annotTypesRequired);
		 */
		FeatureMap features = doc.getFeatures();
		String originalContent = (String) features
				.get(GateConstants.ORIGINAL_DOCUMENT_CONTENT_FEATURE_NAME);
		RepositioningInfo info = (RepositioningInfo) features
				.get(GateConstants.DOCUMENT_REPOSITIONING_INFO_FEATURE_NAME);

		Iterator it = annotSet.iterator();
		Annotation currAnnot;
		long start, end;
		Integer pos;
		String type, category;
		FeatureMap feats;
		while (it.hasNext()) {
			currAnnot = (Annotation) it.next();
			start = currAnnot.getStartNode().getOffset().longValue();
			end = currAnnot.getEndNode().getOffset().longValue();
			Tag tag = new Tag();
			tag.setStart(start);
			tag.setLength((int) (end > 0L ? end - start : end));
			if (start >= 0L && end > start) {
				tag.setContent(text.substring((int) start, (int) end));
			}
			type = currAnnot.getType();
			feats = currAnnot.getFeatures();
			category = (String)feats.get("category");
			tag.setType(type);
			tag.pos = PartOfSpeech.UNKNOWN;
			if ((type.compareToIgnoreCase("Token") == 0) && (category != null)) {
				pos = (Integer)posConversionMap.get(category);
				if (pos != null) 
					tag.pos = pos.intValue();
			}  
			

			TreeMap tagFeats = new TreeMap();
			Iterator it1 = feats.keySet().iterator();
			while (it1.hasNext()) {
				String key = (String) it1.next();
				Object value = feats.get(key);
				tagFeats.put(key, value);
			}
			tag.setFeatures(tagFeats);
			tags.add(tag);
		}		
		Tag[] tagArray = (Tag[])tags.toArray(new Tag[] {});
		Arrays.sort(tagArray);
		return tagArray;
	}

	public static void main(String args[]) throws Exception {
		try {
			GateTagger tagger = new GateTagger();
			tagger.init();
			Tag[] tags = tagger
					.tag("Solar cells made from thin-film technologies could make up about one-third of the fast-growing solar photovoltaic market by 2015, a research firm predicted. NanoMarkets on Monday released a report that forecast a rapid uptake of thin-film photovoltaics with spending set to grow from $1 billion this year to $7.2 million by 2015. U.S. Senator Russell Feingold wants to censure President Bush and possibly others in the Administration over events leading up to Iraq and for the handling of the Iraq War. That would be a terrible idea and a step backwards for the United States.");
			for (int i = 0; i < tags.length; i++) {
				System.out.println(tags[i]);
			}
		} catch (Exception ex) {
			System.out.println(ex.toString());
			ex.printStackTrace();
		}
	}
}
